<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style type="text/css">
    body {
        margin: 0;
        padding: 0;
    }

    .main {
        width: 800px;
        height: 400px;
        margin: 50px auto;
    }

    .btn {
        width: 100px;
        height: 40px;
    }

    .map {
        position: relative;
        width: 800px;
        height: 400px;
        background: #ccc;
    }
</style>

<body>
    <div class="main">
        <button class="btn1" id="start">开始游戏</button>
        <button class="btn2" id="pause">暂停游戏</button>
        <button class="btn3" id="stop">停止游戏</button>
        <div class="map" id="map"></div>
    </div>
</body>

</html>
<script>
    //贪吃蛇的实现思路

    //1.移动的实现思路,将最后一节蛇身体切掉,然后重新计算
    //x移动实现思路
    //由当前蛇头的位置x+1,重新计算left坐标
    //y移动的实现思路,将最后一节蛇身体切掉,然后重新计算,top的值,等于蛇头y的值+1

    //2.吃食物,长长
    //判断蛇头的坐标left top如果和实物的left top重合了,就判断吃到了食物,重新生成一节,添加到数组中

    //3.判定是否撞墙
    //x轴,如果蛇头的x坐标<0,那么就判定撞到了图上的左边边框,判定死亡

    //x轴,如果蛇头x的坐标> map.offsetWidth- 蛇头的offsetWidth 撞上了最右边的边框 也是判定死亡

    //y轴,如果蛇头y的坐标<0,那么就判定撞到了图上的上边边框,判定死亡

    //y轴,如果蛇头y的坐标>map.offsetHeight-蛇头.offsetHeight,那么就判定撞到了图上的下边边框,判定死亡

    //4.判定是否撞到了自己

    //遍历所有的蛇身子,看一下蛇头的left和top是否和其中的一节身体的left top重合 如果重合,判定为撞到了自己


    //5.食物的判定

    //随机生成食物
    //x的范围 0 - map.offsetWidth

    //y的范围 - - map.offsetHeight

    //吃到食物之后 需要重新生成一个食物


    //创建一个公共的类,里面有工具函数
    //以后所有的公共方法都可以放到这里来
    class Tools {
        //创建元素
        _c = tagName => document.createElement(tagName)

        //获取dom元素
        $ = id => document.getElementById(id)
    }


    //蛇蛇继承自Tools 可以获取里面的工具函数
    class Snak extends Tools {
        constructor() {
            super()
            //定义蛇蛇的身子
            this.body = [
                { x: 2, y: 0 },//蛇头
                { x: 1, y: 0 },
                { x: 0, y: 0 } //蛇尾
            ]
            //定义一节蛇的身子宽度和高度
            this.width = 15;
            this.height = 15;
            //将map获取下来
            this.map = this.$('map')
            //蛇的方向 默认往右边跑
            this.direction = 'right'
            //调用绑定键盘事件的方法
            this.keyboardEvent()
        }
        //键盘事件,控制蛇的方向
        keyboardEvent() {
            document.onkeydown = (e) => {
                console.log(e);
                switch (e.keyCode) {
                    //左
                    case 37:
                        this.direction = 'left'
                        break
                    //上
                    case 38:
                        this.direction = 'up'
                        break
                    //右
                    case 39:
                        this.direction = 'right'
                        break
                    //下
                    case 40:
                        this.direction = 'down'
                }
            }
        }
        //画蛇
        display() {
            // this.map.innerHTML = "" //无差别攻击,把蛇和实物全部都干掉,所以不能用这种方法
            //根据蛇的身体数据,将蛇创建出来
            this.body.forEach(item => {
                if (item.oldNode) {
                    this.map.removeChild(item.oldNode)
                }
                let div = this._c('div');
                //给身子添加小粉红背景色
                div.style.backgroundColor = 'hotpink';
                //添加边框
                div.style.border = '1px solid #000'
                //设置宽高
                div.style.width = this.width + 'px';
                div.style.height = this.height + 'px';
                //让蛇蛇拍成一排 设置定位
                div.style.position = 'absolute'
                //left = x*自身宽度
                //top = y*自身高度
                div.style.left = this.width * item.x + 'px'
                div.style.top = this.height * item.y + 'px'
                //将div添加到item上,作为一个旧的节点
                item.oldNode = div;
                this.map.appendChild(div)
            })
        }

        //蛇跑
        run() {
            //先让蛇在水平方向跑起来
            let final = this.body.pop();
            //让蛇头的x轴+1
            //获取蛇头,利用蛇头中的数据,计算下一节身子位置,如果直接修改蛇头的数据,由于蛇头数据是一个对象,引用数据类型
            //对蛇头的数据做一个浅拷贝
            let first = { ...this.body[0] };
            // console.log(first);
            //取出蛇头的x轴和y轴
            //解构赋值
            let { x } = first
            let { y } = first
            console.log(this.direction);
            switch (this.direction) { //判断方向是什么
                case 'up':
                    y -= 1
                    break;
                case 'down':
                    y += 1
                    break;
                case 'left':
                    x -= 1
                    break;
                case 'right':
                    x += 1
            }

            console.log(x, y);
            //赋值给最后一个蛇身子
            final.x = x;
            final.y = y;

            //添加到蛇身数组的第0项
            this.body.unshift(final)

            //重新绘制蛇蛇
            this.display()
        }
    }

    //按钮控制的类
    class BtnController extends Tools {
        constructor(snakContext) {
            super()//使用了extends 一定要使用super
            //获取页面上的按钮
            this.pauseBtn = this.$('pause');
            this.startBtn = this.$('start');
            this.stopBtn = this.$('stop');

            //异常捕获
            //判断 传入的实例是不是Snak这个类的实例
            if (snakContext instanceof Snak) {
                //调用方法,给按钮绑定事件
                //将传入的实例,添加到当前实例的某个属性上
                this.s = snakContext;
                this.btnEvent()
            } else {
                //如果说不是Snak的实例,容易出错,那么我们就可以不给按钮绑定事件,那么就不会报错了
                alert('传入的实例有问题')
            }

        }

        btnEvent = () => {
            this.startBtn.onclick = this.startGame
            this.pauseBtn.onclick = this.pauseGame
            this.stopBtn.onclick = this.stopGame
        }
        //开始游戏
        startGame = () => {
            //调用蛇实例的run方法
            //将定时器添加到当前的实例上
            this.interval = setInterval(() => {
                this.s.run()
            }, 100)
        }
        //暂停游戏
        pauseGame = () => {
            clearInterval(this.interval)
        }
        //停止游戏
        stopGame = () => {
            //1.要让蛇的状态恢复到初始状态
            //由于这里重新赋值了一个新数组,所以导致页面没法移除掉旧的节点,页面中会重复出现蛇蛇
            //所以我们需要将原来旧的节点,也添加到初始化的数据中
            //我们需要做印射
            let initialX = 3;
            let newBody = this.s.body.map(item => {
                initialX--
                return {
                    x: initialX,
                    y: 0,
                    oldNode: item.oldNode

                }
            })
            // console.log(newBody);
            this.s.body = newBody;
            //再次调用snak实例的display方法,重新绘制
            this.s.display()
            //2.也要清除定时器
            clearInterval(this.interval)
        }
    }

    //食物
    class Food { }

    let s = new Snak()

    s.display()
    // console.log(s instanceof Snak);
    // setInterval(() => {
    //     s.run()
    // }, 500)

    new BtnController(s)


</script>